# 指定文件编码为UTF-8
# coding: utf-8

"""
train_convnet.py 功能说明：
1. 训练一个简单的卷积神经网络(CNN)用于MNIST手写数字识别
2. 网络结构：Conv -> ReLU -> Pool -> Affine -> ReLU -> Affine -> Softmax
3. 使用Adam优化器进行训练
4. 记录并可视化训练过程中的准确率变化
5. 保存训练好的模型参数

关键点：
- 输入数据保持原始图像结构(1×28×28)，不展平
- 包含30个5×5的卷积滤波器
- 使用最大池化(2×2，步长2)
- 训练20个epoch，批量大小100
- 使用学习率0.001的Adam优化器
- 每epoch评估1000个样本的准确率
"""

# 导入系统模块
import sys, os
# 添加父目录到系统路径
sys.path.append(os.pardir)
# 导入NumPy数值计算库
import numpy as np
# 导入matplotlib绘图库
import matplotlib.pyplot as plt
# 从dataset.mnist导入MNIST数据加载函数
from dataset.mnist import load_mnist
# 从simple_convnet导入简单卷积网络类
from simple_convnet import SimpleConvNet
# 从common.trainer导入训练器类
from common.trainer import Trainer

# 加载MNIST数据集(不展平，保持图像结构)
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=False)

# 如需快速测试，可减少数据量(取消注释以下两行)
#x_train, t_train = x_train[:5000], t_train[:5000]
#x_test, t_test = x_test[:1000], t_test[:1000]

# 设置最大训练轮数
max_epochs = 20

# 初始化简单卷积网络
network = SimpleConvNet(
    input_dim=(1,28,28),  # 输入维度(通道,高,宽)
    conv_param = {
        'filter_num': 30,   # 卷积滤波器数量
        'filter_size': 5,   # 滤波器尺寸
        'pad': 0,           # 填充大小
        'stride': 1         # 步长
    },
    hidden_size=100,        # 隐藏层神经元数量
    output_size=10,        # 输出层神经元数量
    weight_init_std=0.01    # 权重初始化标准差
)

# 创建训练器实例
trainer = Trainer(
    network,               # 网络实例
    x_train, t_train,      # 训练数据
    x_test, t_test,        # 测试数据
    epochs=max_epochs,     # 训练轮数
    mini_batch_size=100,   # 批量大小
    optimizer='Adam',      # 使用Adam优化器
    optimizer_param={'lr': 0.001},  # 学习率0.001
    evaluate_sample_num_per_epoch=1000  # 每epoch评估1000个样本
)

# 开始训练
trainer.train()

# 保存训练好的参数到文件
network.save_params("params.pkl")
print("Saved Network Parameters!")

# 绘制训练过程中的准确率变化曲线
markers = {'train': 'o', 'test': 's'}  # 标记样式
x = np.arange(max_epochs)  # x轴数据(epoch数)
# 绘制训练集准确率曲线(圆圈标记)
plt.plot(x, trainer.train_acc_list, marker='o', label='train', markevery=2)
# 绘制测试集准确率曲线(方块标记)
plt.plot(x, trainer.test_acc_list, marker='s', label='test', markevery=2)
# 设置坐标轴标签
plt.xlabel("epochs")
plt.ylabel("accuracy")
# 设置y轴范围
plt.ylim(0, 1.0)
# 显示图例
plt.legend(loc='lower right')
# 显示图形
plt.show()

"""
训练过程说明：
1. 每个epoch包含600次迭代(60000训练数据/100批量大小)
2. 每epoch结束时评估1000个训练样本和1000个测试样本的准确率
3. Adam优化器自动调整学习率，通常比SGD收敛更快
4. 训练完成后参数保存到params.pkl文件

预期结果：
1. 训练准确率应随epoch增加而提高
2. 测试准确率应与训练准确率接近(避免过拟合)
3. 最终测试准确率应在98%以上

注意事项：
1. 完整训练可能需要较长时间(约10-20分钟)
2. 可调整学习率、批量大小等超参数观察效果
3. 如需快速测试，可减少数据量和epoch数
"""
